package peerToPeerFacebook.proj;

import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;

/** Class that can wait for results from multiple sources. */
public class ResultGatherer
{
	/** Holder class for holding single result */
	public class Result
	{
		public Status status = null;
		public Serializable result = null;
		
		public Result(Status status, Serializable result)
		{
			this.status = status;
			this.result = result;
		}
	}
	
	/** List of results that are obtained (non null) or ones we are trying to obtain (null). */
	protected List<Result> results = new LinkedList<ResultGatherer.Result>();
	
	/** Callback that should be triggered when all results are set */
	protected IResultCallback callback = null;
	
	/** COnstructs instance of the class */
	public ResultGatherer()
	{ }
	
	/** Creates {@link IResultCallback} that should be passed into functions that accepts it. In general each time this is
	 * called new gatherer is created with unique index. This index will be used to pass result to callback when
	 * callbacks are triggered.
	 * 
	 * @return {@link IResultCallback} That should be used for methods accepting IResultCallback
	 */
	public IResultCallback createGatherer()
	{
		final int id = results.size();
		results.add(null);

		return new IResultCallback()
		{
			@Override
			public void resultCallback(Status status, Serializable result)
			{
				// Update result entrie and check if we are done with the results\
				results.set(id, new Result(status, result));
				checkAllResults();
			}
		};
	}
	
	/** Callback will be triggered when all callbacks created by createGatherer are triggered. In such case passed in 
	 * status will be success and result will be of type:
	 * List<Result>. Where i-th item in the list would be result passed in to i-th callback returned by createGatherer.
	 *  
	 * @param callback callback to call when all results are gathered.
	 */
	public void waitAll(IResultCallback callback)
	{
		// yeah, thus is just it. checkAllResults will take care to trigger it.
		this.callback = callback; 
		
		// In some cases (i.e. no real assync operation) it is possible that we already have results. If so we should
		// return imeadetely.
		checkAllResults();
	}
	
	/** Checks that maybe we already have all results. If so invokes callback passed into waitAll.
	 */
	protected void checkAllResults()
	{
		for (Result res: results)
			if (res == null)
				// we are missing something
				return;
		
		// hey all are ready. Let's ping caller. It is possible that we receive callbacks before result callback is set.
		if (callback != null)
			callback.resultCallback(Status.Success, (Serializable) results);
	}
}
